﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using snat.model;
using System.IO;
using System.Reflection;
using snat.util;
using snat.algorithms;

namespace algorithms.algorithms.MessageRates {
    public class GlobalMessageRate : IAlgorithm, IChartRenderable, IReaderWriter {

        private TimeSpan intervalSpan;
        private DateTime currentInterval;

        public Dictionary<DateTime, int> Rate {get; private set;}

        public string Name {
            get { return "Message Rates " + this.intervalSpan.ToString(); }
        }

        public GlobalMessageRate(TimeSpan _interval, int? _nodeID=null) {
            this.intervalSpan = _interval;
        }

        public void OnTapeLoaded() {
            currentInterval = NetworkModel.Instance.StartTime;
            this.Rate = new Dictionary<DateTime, int>();
            Rate.Add(currentInterval, 0);
        }

        public void NewMessage(Message m) {
            while (m.Timestamp >= currentInterval + intervalSpan) {
                currentInterval += intervalSpan;
                Rate.Add(currentInterval, 0);
            }
            Rate[currentInterval]++;
        }

        public void Output() {
            if(RenderChart != null) RenderChart(this);
        }

        public void Register() {
            NetworkModel.Instance.TapeLoaded += OnTapeLoaded;
            NetworkModel.Instance.MessageProcessed += NewMessage;
            NetworkModel.Instance.TapePaused += Process;
            NetworkModel.Instance.TapeEnded += Process;
        }

        private void Process() {
            if(RenderChart != null) RenderChart(this);
        }

        public void DeRegister() {
            NetworkModel.Instance.TapeLoaded -= OnTapeLoaded;
            NetworkModel.Instance.MessageProcessed -= NewMessage;
        }

        public event RenderChartDelegate RenderChart;

        public snat.util.DataChart GetRenderableChart() {
            DataSeries series1 = new DataSeries("Message Rate");
           
            int numOfWindows = 25;
            List<DateTime> dates = Rate.Keys.ToList();
            List<int> msgrates = Rate.Values.ToList();
            int step = dates.Count / numOfWindows;
            if (step != 0) {
                List<int> windowedAvg = new List<int>();
                for (int i = 0; i < dates.Count; i++) {
                    windowedAvg.Add(msgrates[i]);

                    // If we are at a step point, take the average rate of the window, add it to the series and reset the list
                    if (i % step == 0) {
                        series1.Series.Add(new DataPoint(dates[i].ToShortDateString(), windowedAvg.Average()));
                        windowedAvg.Clear();
                    } else {
                        // Else, if we are on the latest element, just take the running average
                        if (i == dates.Count - 1) {
                            series1.Series.Add(new DataPoint(dates[i].ToShortDateString(), windowedAvg.Average()));
                        }
                    }
                }
            }

            DataChartOptions chartOpts = new DataChartOptions("Global Message Rate Chart");
            chartOpts.Description = "This chart gives the global Message Rates for network in time-windows. Message Rate is a measure of the activity of the network over time. The dates on the x-axis signify the start of the respective time window.";
            return new DataChart(chartOpts, new List<DataSeries> { series1 });
        }

        public void Write(string filePath) {
            using (StreamWriter output = new StreamWriter(filePath)) {
                foreach (DateTime t in Rate.Keys) {
                    output.WriteLine("{0}, {1}", t.ToShortDateString(), Rate[t]);
                }
            }
        }

        public string Read(string filePath) {
            StringBuilder ret = new StringBuilder();
            using (StreamReader input = new StreamReader(filePath)) {
                while (!input.EndOfStream) {
                    ret.Append(input.ReadLine());
                }
            }
            return ret.ToString();
        }
    }
}
